/*
 * Copyright (c) 2013 Google, Inc
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <asm/arch/hardware.h>

#define OFFSET_GPIO_DATA               (0x0000)	//get data or set data
#define OFFSET_GPIO_DMSK               (0x0004)	//mask when set data
#define OFFSET_GPIO_DIR                (0x0008)	//set direction
#define OFFSET_GPIO_IS                 (0x000C)	//interrupt sense(level/edge)
#define OFFSET_GPIO_IBE                (0x0010)	//interrupt both edges
#define OFFSET_GPIO_IEV                (0x0014)	//interrupt event high/low
#define OFFSET_GPIO_IE                 (0x0018)	//interrupt enable
#define OFFSET_GPIO_RIS                (0x001C) //raw interrupt status
#define OFFSET_GPIO_MIS                (0x0020)	//masked interrupt status
#define OFFSET_GPIO_IC                 (0x0024) //interrupt clear
#define OFFSET_GPIO_INEN               (0x0028)	//input enable
#define OFFSET_GPIO_DB_EN              (0x0030)	//debounce enable
#define OFFSET_GPIO_DB_BOTH            (0x0034)	//debounce both
#define OFFSET_GPIO_DB_INV             (0x0038) //debounce inverse

static struct array_bank{
		uint base;
		uint dr_offset;
		uint msk_offset;
		uint ddr_offset;
		uint ien_offset;
		uint ext_offset ;
} Bank[] = {
		{
				.base = GPIO0_REG_BASE,
				.dr_offset = OFFSET_GPIO_DATA,
				.msk_offset = OFFSET_GPIO_DMSK,
				.ddr_offset = OFFSET_GPIO_DIR,
				.ien_offset = OFFSET_GPIO_INEN,
				.ext_offset = OFFSET_GPIO_DATA,
		},
		{
				.base = GPIO1_REG_BASE,
				.dr_offset = OFFSET_GPIO_DATA,
				.msk_offset = OFFSET_GPIO_DMSK,
				.ddr_offset = OFFSET_GPIO_DIR,
				.ien_offset = OFFSET_GPIO_INEN,
				.ext_offset = OFFSET_GPIO_DATA,
		},
#ifdef GPIO2_REG_BASE
		{
				.base = GPIO2_REG_BASE,
				.dr_offset = OFFSET_GPIO_DATA,
				.msk_offset = OFFSET_GPIO_DMSK,
				.ddr_offset = OFFSET_GPIO_DIR,
				.ien_offset = OFFSET_GPIO_INEN,
				.ext_offset = OFFSET_GPIO_DATA,
		},
#endif
#ifdef GPIO3_REG_BASE
		{
				.base = GPIO3_REG_BASE,
				.dr_offset = OFFSET_GPIO_DATA,
				.msk_offset = OFFSET_GPIO_DMSK,
				.ddr_offset = OFFSET_GPIO_DIR,
				.ien_offset = OFFSET_GPIO_INEN,
				.ext_offset = OFFSET_GPIO_DATA,
		},
#endif
#ifdef GPIO4_REG_BASE
		{
				.base = GPIO4_REG_BASE,
				.dr_offset = OFFSET_GPIO_DATA,
				.msk_offset = OFFSET_GPIO_DMSK,
				.ddr_offset = OFFSET_GPIO_DIR,
				.ien_offset = OFFSET_GPIO_INEN,
				.ext_offset = OFFSET_GPIO_DATA,
		},
#endif
#ifdef GPIO5_REG_BASE
		{
				.base = GPIO5_REG_BASE,
				.dr_offset = OFFSET_GPIO_DATA,
				.msk_offset = OFFSET_GPIO_DMSK,
				.ddr_offset = OFFSET_GPIO_DIR,
				.ien_offset = OFFSET_GPIO_INEN,
				.ext_offset = OFFSET_GPIO_DATA,
		},
#endif
};

int gpio_request(unsigned gpio, const char *label)
{
	return 0;
}

int gpio_free(unsigned gpio)
{
	return 0;
}

int gpio_direction_input(unsigned gpio)
{
	int bank, no;
	int dir, msk;
	int inen;

	if (gpio >= 0 && gpio < CONFIG_GPIO_NUMBER)
	{
		bank = gpio / 8 ;
		no   = gpio % 8;
		dir  = GET_REG(Bank[bank].base + Bank[bank].ddr_offset);
		dir &= ~(0x1 << no);
		msk = GET_REG(Bank[bank].base + Bank[bank].msk_offset);
		msk |= (0x1 << no);
		inen = GET_REG(Bank[bank].base + Bank[bank].ien_offset);
		inen |= (0x1 << no);
		SET_REG(Bank[bank].base + Bank[bank].ddr_offset, dir);
		SET_REG(Bank[bank].base + Bank[bank].msk_offset, msk);
		SET_REG(Bank[bank].base + Bank[bank].ien_offset, inen);
		return 0;
	}

	return -1;
}

int gpio_direction_output(unsigned gpio, int value)
{
	int bank, no;
	int dir, msk, val, inen;

	if (gpio >= 0 && gpio < CONFIG_GPIO_NUMBER)
	{
		bank = gpio / 8 ;
		no   = gpio % 8;

		val = GET_REG(Bank[bank].base + Bank[bank].dr_offset);
		if (value == 1)
			val |= (0x1 << no);
		else if (value == 0)
			val &= ~(0x1 << no);

		dir  = GET_REG(Bank[bank].base + Bank[bank].ddr_offset);
		dir |= (0x1 << no);
		msk = GET_REG(Bank[bank].base + Bank[bank].msk_offset);
		msk |= (0x1 << no);
		inen = GET_REG(Bank[bank].base + Bank[bank].ien_offset);
		inen &= ~(0x1 << no);
		SET_REG(Bank[bank].base + Bank[bank].ien_offset, inen);
		SET_REG(Bank[bank].base + Bank[bank].ddr_offset, dir);
		SET_REG(Bank[bank].base + Bank[bank].msk_offset, msk);
		SET_REG(Bank[bank].base + Bank[bank].dr_offset, val);

		return 0;
	}

	return -1;
}

int gpio_get_value(unsigned gpio)
{
	int bank, no;
	int value;

	if (gpio >= 0 && gpio < CONFIG_GPIO_NUMBER)
	{
		bank  = gpio / 8 ;
		no    = gpio % 8;
		value = GET_REG(Bank[bank].base + Bank[bank].ext_offset) & (0x1 << no);
		return value ? 1 : 0;
	}

	return -1;
}

int gpio_set_value(unsigned gpio, int value)
{
	return gpio_direction_output(gpio, value);
}

